/*
 * @author: Archy
 * @Date: 2022-03-11 13:28:16
 * @LastEditors: Archy
 * @LastEditTime: 2022-03-15 10:55:12
 * @FilePath: \arkgen\react\src\hooks\useTerminal.ts
 * @description: terminal控制台的hooks
 */
import { Terminal } from "xterm";
import { useRef, useEffect } from "react";

const terminalIns: Terminal & { onDataAdded?: boolean } = new Terminal({
  rendererType: 'canvas',
  convertEol: true,
  scrollback: 50,
  disableStdin: false,
  cursorStyle: 'underline',
  cursorBlink: true,
  theme: {
    foreground: '#f0f0f0',
    background: '#002833',
    cursor: 'help',
  }
})

export enum ReadyState {
  Connecting = 0,
  Open = 1,
  Closing = 2,
  Closed = 3,
}

class WebSocketShims extends WebSocket {
  private _eventAdded: boolean = false
  constructor(url: string | URL, protocols?: string | string[] | undefined) {
    super(url, protocols)
  }
  set eventAdded(state: boolean) {
    this._eventAdded = state
  }
  get eventAdded() {
    return this._eventAdded
  }
}

const wsInstance = new WebSocketShims(import.meta.env.VITE_WS_URL + '/terminal')
/**
 * @description: 
 * @param {*} url websocket url
 * @return {*}
 */
export const useTerminal = (): [Terminal, WebSocket | undefined, (dom: HTMLDivElement) => void] => {
  const terminal = useRef(terminalIns)
  const inputMessage = useRef<string>('')
  const cwd = useRef<string>('')

  const prompt = () => {
    inputMessage.current = ''
    terminal.current.write(`\r\n${cwd.current ?? ''}> `);
  }

  if (!wsInstance.eventAdded) {
    terminal.current.writeln('connecting...')
    wsInstance.onmessage = ((message: WebSocketEventMap['message']) => {
      if (message?.data?.split('&')[0] === 'cwdchange') {
        cwd.current = message.data?.split('&')[1]
        return
      }
      if (message?.data === 'PROMPT') {
        prompt()
        return
      }

      if (message?.data !== undefined) {
        terminal.current.writeln(message?.data)
      }
    })
    wsInstance.onopen = (() => {
      terminal.current.writeln('connected.')
    })
    wsInstance.onclose = (()=>{
      terminal.current.writeln('closed.')
    })
    wsInstance.eventAdded = true
  }

  if (!terminal.current.onDataAdded) {
    terminal.current.onData(e => {
      switch (e) {
        case '\u0003':
          terminal.current.write('^C');
          wsInstance.send('^C')
          break;
        case '\r':
          if (inputMessage.current) {
            wsInstance.send(inputMessage.current)
            terminal.current.writeln('')
            inputMessage.current = ''
          } else {
            prompt()
          }
          break;
        case '\u007F':
          if (inputMessage.current.length > 0) {
            terminal.current.write('\b \b');
            inputMessage.current = inputMessage.current.substring(0, inputMessage.current.length - 1);
          }
          break;
        default:
          if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7B) || e >= '\u00a0') {
            terminal.current.write(e)
            inputMessage.current += e
          }
      }
    });
    terminal.current.onDataAdded = true
  }

  const setTerminal = (dom: HTMLDivElement) => {
    terminal.current.open(dom)
  }

  return [terminal.current, wsInstance, setTerminal]
}

